﻿using Microsoft.AspNetCore.Http;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WLYT.Application.Interfaces;
using WLYT.Core;
using WLYT.Domain.Entries;
using WLYT.Domain.Enum;
using WLYT.Domain.Model;
using WLYT.Redis;

namespace WLYT.Application.Services
{
    public class AgvService(IRepository<Sys_AgvMap> sysAgvMapRepository,
                            IRepository<AgvMapStatus> agvMapStatusRepository,
                            IRepository<AgvTask> agvTaskRepository,
                            IRepository<AgvDetailTask> agvDetailTaskRepository,
                            IHikService hikService,
                            IRedisService redisService) : IAgvService
    {
        /// <summary>
        /// 设备状态
        /// </summary>
        /// <param name="plcAddress"></param>
        /// <param name="status">0：无治具，1：有治具有料，2；有治具无料</param>
        /// <returns></returns>
        public async Task<ApiResult> DeviceStatus(string plcAddress, bool isRun)
        {
            ApiResult apiResult = new ApiResult();
            await agvMapStatusRepository.UpdateAsync(p => new AgvMapStatus { IsRun = isRun }, p => p.PLCStatusAddress == plcAddress);
            return apiResult;
        }

        /// <summary>
        /// 状态改变
        /// </summary>
        /// <param name="plcAddress"></param>
        /// <param name="status">0：无治具，1：有治具有料，2；有治具无料</param>
        /// <returns></returns>
        public async Task<ApiResult> StatusChange(string plcAddress, int status, int size)
        {
            ApiResult apiResult = new ApiResult();
            var map = await agvMapStatusRepository.GetFirstAsync(p => p.PLCAddress == plcAddress);
            if (map == null)
            {
                apiResult.Code = 1;
                apiResult.Message = $"{plcAddress}地址不存在";
                return apiResult;
            }
            if (map.Status == (int)MapStatus.入料中 || map.Status == (int)MapStatus.出料中)
            {
                apiResult.Code = 1;
                apiResult.Message = $"地址【{plcAddress}】入料或出料中，不能更新状态";
                return apiResult;
            }
            if (map.Status != status)
            {
                await agvMapStatusRepository.UpdateAsync(p => new AgvMapStatus { Status = status, Size = size }, p => p.PLCAddress == plcAddress);
            }
            return apiResult;
        }

        /// <summary>
        /// 生成AGV任务
        /// </summary>
        /// <returns></returns>
        public async Task CreateAgvTask()
        {
            await CreateAgvTask_DLJ_Out();
            await CreateAgvTask_DLJ_In_2();
            await CreateAgvTask_Warehouse();
            await CreateAgvTask_DLJ_In();
        }

        /// <summary>
        /// 生成点料机_出料口 AGV任务
        /// </summary>
        /// <returns></returns>
        private async Task CreateAgvTask_DLJ_Out()
        {
            var map = await agvMapStatusRepository.GetFirstAsync(p => p.IsRun && p.DeviceType == (int)DeviceEnum.点料机_出料 && (p.Status == (int)MapStatus.有治具有料));
            if (map == null) return;
            if (map.Size == 7)
            {
                var warehouse = await agvMapStatusRepository.GetFirstAsync(p => p.IsRun && p.DeviceType == (int)DeviceEnum.智能仓 && (p.Status == (int)MapStatus.无治具 || p.Status == (int)MapStatus.有治具无料));
                if (warehouse == null) return;
                if (warehouse.Status == (int)MapStatus.有治具无料)
                {
                    var recycleMap = await agvMapStatusRepository.GetFirstAsync(p => p.IsRun && (p.DeviceType == (int)DeviceEnum.立库 || p.DeviceType == (int)DeviceEnum.缓存台) && p.Status == (int)MapStatus.无治具);
                    if (recycleMap == null)
                    {
                        return;
                    }
                    //点料机_出料=》智能仓下料 =》 智能仓上料 =》 缓存台
                    await CallAgv(map.PLCAddress, warehouse.PLCAddress, warehouse.PLCAddress, recycleMap.PLCAddress);
                    return;
                }
                else
                {
                    //点料机_出料=》智能仓 
                    await CallAgv(map.PLCAddress, warehouse.PLCAddress, AgvDetailType.上治具_有料);
                    return;
                }
            }
            else
            {
                var tempMap = await agvMapStatusRepository.GetFirstAsync(p => p.IsRun && p.DeviceType == (int)DeviceEnum.缓存台 && p.Status == (int)MapStatus.无治具);
                if (tempMap == null)
                {
                    return;
                }
                //点料机_出料 =》 缓存台
                await CallAgv(map.PLCAddress, tempMap.PLCAddress, AgvDetailType.上治具_有料);
            }
        }

        /// <summary>
        /// 生成点料机_入料口 AGV任务
        /// </summary>
        /// <returns></returns>
        private async Task CreateAgvTask_DLJ_In()
        {
            var dlj_In = await agvMapStatusRepository.GetFirstAsync(p => p.IsRun && p.DeviceType == (int)DeviceEnum.点料机_进料 && p.Status == (int)MapStatus.无治具);
            if (dlj_In == null) return;
            var dlj_Out = await agvMapStatusRepository.GetFirstAsync(p => p.IsRun && p.DeviceType == (int)DeviceEnum.点料机_出料 && p.Status == (int)MapStatus.无治具);
            if (dlj_Out == null) return;
            var tempMap = await agvMapStatusRepository.GetFirstAsync(p => p.IsRun && p.DeviceType == (int)DeviceEnum.缓存台 && p.Status == (int)MapStatus.有治具有料);
            if (tempMap == null) return;
            var tempMap_Out = await agvMapStatusRepository.GetFirstAsync(p => p.IsRun && p.DeviceType == (int)DeviceEnum.缓存台 && p.Size == tempMap.Size && p.Status == (int)MapStatus.有治具无料);
            if (tempMap_Out == null) return;
            //缓存台下料 =》 缓存台下料 =》 点料机_进料上料 =》 点料机_出料上料
            await CallAgv(tempMap.PLCAddress, tempMap_Out.PLCAddress, dlj_In.PLCAddress, dlj_Out.PLCAddress);

        }

        /// <summary>
        /// 生成点料机_入料口_下料 AGV任务
        /// </summary>
        /// <returns></returns>
        private async Task CreateAgvTask_DLJ_In_2()
        {
            var map = await agvMapStatusRepository.GetFirstAsync(p => p.IsRun && p.DeviceType == (int)DeviceEnum.点料机_进料 && p.Status == (int)MapStatus.有治具无料);
            if (map == null)
            {
                return;
            }
            var tempMap = await agvMapStatusRepository.GetFirstAsync(p => p.IsRun && p.DeviceType == (int)DeviceEnum.缓存台 && p.Status == (int)MapStatus.无治具);
            if (tempMap == null)
            {
                return;
            }
            //点料机_入料口 =》 缓存台
            await CallAgv(map.PLCAddress, tempMap.PLCAddress, AgvDetailType.上治具_无料);
        }

        /// <summary>
        /// 生成立库 AGV任务
        /// </summary>
        /// <returns></returns>
        private async Task CreateAgvTask_Warehouse()
        {
            var map = await agvMapStatusRepository.GetFirstAsync(p => p.IsRun && p.DeviceType == (int)DeviceEnum.立库 && p.Status == (int)MapStatus.有治具有料);
            if (map == null) return;
            var warehouse = await agvMapStatusRepository.GetFirstAsync(p => p.IsRun && p.DeviceType == (int)DeviceEnum.智能仓 && (p.Status == (int)MapStatus.无治具 || p.Status == (int)MapStatus.有治具无料));
            if (warehouse == null) return;
            if (warehouse.Status == (int)MapStatus.有治具无料)
            {
                //立库=》智能仓 => 智能仓 =》 立库
                await CallAgv(map.PLCAddress, warehouse.PLCAddress, warehouse.PLCAddress, map.PLCAddress);
                return;
            }
            else
            {
                //立库=》智能仓 
                await CallAgv(map.PLCAddress, warehouse.PLCAddress, AgvDetailType.上治具_有料);
                return;
            }
        }

        /// <summary>
        /// 呼叫AGV
        /// </summary>
        /// <param name="plcPositoin1"></param>
        /// <param name="plcPositoin2"></param>
        /// <param name="plcPositoin3"></param>
        /// <param name="plcPositoin4"></param>
        /// <returns></returns>
        private async Task<ApiResult> CallAgv(string plcPositoin1, string plcPositoin2, string plcPositoin3, string plcPositoin4)
        {
            var mapList = await sysAgvMapRepository.GetListAsync(true);
            string taskCode = "WLYT" + DateTime.Now.ToString("yyyyyMMddHHmmssfff");
            var position1 = mapList.FirstOrDefault(p => p.PLCAddress == plcPositoin1 && p.MapType == (int)MapType.In);
            var position2 = mapList.FirstOrDefault(p => p.PLCAddress == plcPositoin2 && p.MapType == (int)MapType.Out);
            var position3 = mapList.FirstOrDefault(p => p.PLCAddress == plcPositoin3 && p.MapType == (int)MapType.In);
            var position4 = mapList.FirstOrDefault(p => p.PLCAddress == plcPositoin4 && p.MapType == (int)MapType.Out);
            ApiResult apiResult = await hikService.GenAgvSchedulingTask(taskCode, "", new List<string> { position1.Code, position2.Code, position3.Code, position4.Code });
            if (apiResult.Code == 0)
            {
                List<AgvDetailTask> detailTasks =
                [
                    new AgvDetailTask
                    {
                        Sort = 1,
                        Position = position1.Code,
                        MapType = position1.MapType,
                        Enum = position1.Enum,
                        PLCAddress = plcPositoin1,
                        TaskCode = taskCode,
                        Type = (int)AgvDetailType.下料
                    },
                    new AgvDetailTask
                    {
                        Sort = 2,
                        Position = position2.Code,
                        MapType = position2.MapType,
                        Enum = position2.Enum,
                        PLCAddress = plcPositoin2,
                        TaskCode = taskCode,
                        Type = (int)AgvDetailType.下料
                    },
                    new AgvDetailTask
                    {
                        Sort = 3,
                        Position = position3.Code,
                        MapType = position3.MapType,
                        Enum = position3.Enum,
                        PLCAddress = plcPositoin3,
                        TaskCode = taskCode,
                        Type = (int)AgvDetailType.上治具_有料
                    },
                    new AgvDetailTask
                    {
                        Sort = 4,
                        Position = position4.Code,
                        MapType = position4.MapType,
                        Enum = position4.Enum,
                        PLCAddress = plcPositoin4,
                        TaskCode = taskCode,
                        Type = (int)AgvDetailType.上治具_无料
                    },
                ];
                await agvDetailTaskRepository.InsertRangeAsync(detailTasks);
                await agvTaskRepository.InsertAsync(new AgvTask
                {
                    StartPosition = position1.Code,
                    EndPosition = position3.Code,
                    TaskCode = taskCode,
                });
                await agvMapStatusRepository.UpdateAsync(p => new AgvMapStatus { Status = (int)MapStatus.出料中 }, p => p.PLCAddress == plcPositoin1);
                await agvMapStatusRepository.UpdateAsync(p => new AgvMapStatus { Status = (int)MapStatus.出料中 }, p => p.PLCAddress == plcPositoin2);
                if (plcPositoin2 != plcPositoin3)
                {
                    await agvMapStatusRepository.UpdateAsync(p => new AgvMapStatus { Status = (int)MapStatus.入料中 }, p => p.PLCAddress == plcPositoin3);
                }
                if (plcPositoin1 != plcPositoin4)
                {
                    await agvMapStatusRepository.UpdateAsync(p => new AgvMapStatus { Status = (int)MapStatus.入料中 }, p => p.PLCAddress == plcPositoin4);
                }
            }
            return apiResult;
        }

        /// <summary>
        /// 呼叫AGV
        /// </summary>
        /// <param name="plcPositoin1"></param>
        /// <param name="plcPositoin2"></param>
        /// <returns></returns>
        private async Task<ApiResult> CallAgv(string plcPositoin1, string plcPositoin2, AgvDetailType type)
        {
            string taskCode = "WLYT" + DateTime.Now.ToString("yyyyyMMddHHmmssfff");
            var mapList = await sysAgvMapRepository.GetListAsync(true);
            var startPosition = mapList.FirstOrDefault(p => p.PLCAddress == plcPositoin1 && p.MapType == (int)MapType.In);
            var endPosition = mapList.FirstOrDefault(p => p.PLCAddress == plcPositoin2 && p.MapType == (int)MapType.In);
            ApiResult apiResult = await hikService.GenAgvSchedulingTask(taskCode, "", new List<string> { startPosition.Code, endPosition.Code });
            if (apiResult.Code == 0)
            {
                List<AgvDetailTask> detailTasks =
                [
                    new AgvDetailTask
                    {
                        Sort = 1,
                        Position = startPosition.Code,
                        MapType = startPosition.MapType,
                        Enum = startPosition.Enum,
                        PLCAddress = plcPositoin1,
                        TaskCode = taskCode,
                        Type = (int)AgvDetailType.下料,
                    },
                    new AgvDetailTask
                    {
                        Sort = 2,
                        Position = endPosition.Code,
                        MapType = endPosition.MapType,
                        Enum = endPosition.Enum,
                        PLCAddress = plcPositoin2,
                        TaskCode = taskCode,
                        Type = (int)type,
                    },
                ];
                await agvDetailTaskRepository.InsertRangeAsync(detailTasks);
                await agvTaskRepository.InsertAsync(new AgvTask
                {
                    StartPosition = startPosition.Code,
                    EndPosition = endPosition.Code,
                    TaskCode = taskCode,
                });
                await agvMapStatusRepository.UpdateAsync(p => new AgvMapStatus { Status = (int)MapStatus.出料中 }, p => p.PLCAddress == plcPositoin1);
                await agvMapStatusRepository.UpdateAsync(p => new AgvMapStatus { Status = (int)MapStatus.入料中 }, p => p.PLCAddress == plcPositoin2);
            }
            return apiResult;
        }

        /// <summary>
        /// 绑定小车
        /// </summary>
        /// <param name="taskCode"></param>
        /// <param name="agvCode"></param>
        /// <returns></returns>
        public async Task BindAgv(string taskCode, string agvCode)
        {
            await agvDetailTaskRepository.UpdateAsync(p => new AgvDetailTask { AgvCode = agvCode }, p => p.TaskCode == taskCode);
            await agvTaskRepository.UpdateAsync(p => new AgvTask { AgvCode = agvCode, Status = (int)AgvStatus.运输中 }, p => p.TaskCode == taskCode);
        }

        /// <summary>
        /// 到达位置
        /// </summary>
        /// <param name="taskCode"></param>
        /// <param name="position"></param>
        /// <returns></returns>
        public async Task Arrive(string taskCode, string position)
        {
            var list = await agvDetailTaskRepository.GetListAsync(p => p.TaskCode == taskCode);
            if (list.Count == 0) return;
            var positionDetail = list.FirstOrDefault(p => p.Position == position);
            if (positionDetail == null) return;
            await agvDetailTaskRepository.UpdateAsync(p => new AgvDetailTask { Status = (int)AgvPositionStatus.到达位置 }, p => p.TaskCode == taskCode && p.Position == position);
            //通知主PLC可下料或上料
            await redisService.SetAsync(positionDetail.AgvCode, new { positionDetail.MapType, positionDetail.Type, positionDetail.Enum });
        }

        /// <summary>
        /// 上料或下料动作完成
        /// </summary>
        /// <param name="position"></param>
        /// <returns></returns>
        public async Task<ApiResult> ActionCompleted(string plcAddress)
        {
            ApiResult apiResult = new();
            var positionDetail = await agvDetailTaskRepository.GetFirstAsync(p => p.PLCAddress == plcAddress && p.Status == (int)AgvPositionStatus.到达位置);
            if (positionDetail == null)
            {
                apiResult.Message = $"没有该位置【{plcAddress}】的动作";
                return apiResult;
            }

            var list = await agvDetailTaskRepository.GetListAsync(p => p.TaskCode == positionDetail.TaskCode);
            await agvDetailTaskRepository.UpdateAsync(p => new AgvDetailTask { Status = (int)AgvPositionStatus.动作完成, CompleteTime = DateTime.Now }, p => p.TaskCode == positionDetail.TaskCode && p.Sort == positionDetail.Sort);
            var hasNextTask = list.Any(p => p.PLCAddress == positionDetail.PLCAddress && p.Status == (int)AgvPositionStatus.等待);

            switch (positionDetail.Type)
            {
                case (int)AgvDetailType.下料:
                    await agvMapStatusRepository.UpdateAsync(p => new AgvMapStatus { Status = hasNextTask ? (int)MapStatus.入料中 : (int)MapStatus.无治具 }, p => p.PLCAddress == positionDetail.PLCAddress);
                    break;
                case (int)AgvDetailType.上治具_有料:
                    await agvMapStatusRepository.UpdateAsync(p => new AgvMapStatus { Status = hasNextTask ? (int)MapStatus.出料中 : (int)MapStatus.有治具有料 }, p => p.PLCAddress == positionDetail.PLCAddress);
                    break;
                case (int)AgvDetailType.上治具_无料:
                    await agvMapStatusRepository.UpdateAsync(p => new AgvMapStatus { Status = hasNextTask ? (int)MapStatus.出料中 : (int)MapStatus.有治具无料 }, p => p.PLCAddress == positionDetail.PLCAddress);
                    break;
            }

            if (list.Count == positionDetail.Sort)
            {
                await agvTaskRepository.UpdateAsync(p => new AgvTask { Status = (int)AgvStatus.任务完成, CompleteTime = DateTime.Now }, p => p.TaskCode == positionDetail.TaskCode);
            }
            //通知AGV可继续任务
            apiResult = await hikService.ContinueTask(positionDetail.TaskCode);
            return apiResult;
        }
    }
}
